package is.glif.fenius.rrm;

import java.net.HttpURLConnection;
import java.net.URL;
import java.net.Authenticator;
import java.util.Map;
import java.util.Iterator;
import java.util.Hashtable;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.Enumeration;
import java.text.SimpleDateFormat;
import java.text.FieldPosition;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.*;

import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.DatatypeConfigurationException;

public class Proxy extends Thread
{
    static public boolean DEBUG = false;
    static public int fibreBW = 1000000;
    static public int BW_ACTIVE = 1000000;
    static public int BW_ACTIVE_WAIT = 1000000;
    //static public int BW_ACTIVE_WAIT = 400000;
    static public long remake = 5000;

    static private String username = "superagent";
    static private String password = "glif";

    // static private String saUrl = "http://192.50.75.194:8080/list";
    static private String saUrl = "http://patdev3.internet2.edu:8080/list";
    static private int INTERVAL = 10000;

    Hashtable<String, String> alias = null;
    Hashtable<String, String> rrmxml = null;
    JSONArray fibre = null;
    SimpleDateFormat df = null;
    SimpleDateFormat df2 = null;

    private JSONArray array = null;
    private String rrminfo = null;
    private Date maked = null;

    static
    {
	TimeZone.setDefault(TimeZone.getTimeZone( "UTC"));
    }

    static private Proxy proxy = null;

    static public Proxy getInstance()
    {
	if (proxy == null) {
	    proxy = new Proxy();
	    proxy.start();
	}
	return proxy;
    }

    private Proxy() 
    {
	alias = ConfigHolder.getAlias();
	fibre = ConfigHolder.getFibre();
	df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
	df2 = new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy");
    }

    public void run()
    {
	URL url = null;
	String keepString = "";

	try {
	    url = new URL(saUrl);
	    HttpAuthenticator auth = new HttpAuthenticator(username, password);
	    Authenticator.setDefault(auth);
	} catch (Exception ex) {
	    ex.printStackTrace();
	    return;
	}
	
	while(true) {
	    try {
		if (DEBUG || true) System.err.println
		    ("=========== get status: " + saUrl);
		
		
		HttpURLConnection conn = 
		    (HttpURLConnection)url.openConnection();
		conn.setRequestMethod("GET");
		conn.connect();
		
		/*
		  Map headers = conn.getHeaderFields();
		  Iterator it = headers.keySet().iterator();
		  while (it.hasNext()) {
		  String key= (String) it.next();
		  }
		*/
		
		BufferedReader reader = new BufferedReader
		    (new InputStreamReader(conn.getInputStream()));
		
		String objectString = "";
		String line = null;
		while ((line = reader.readLine()) != null) {
		    objectString += " " + line;
		}

		if (DEBUG || true) System.err.println
		    ("=========== get status: done");

		if ((keepString == null) || (! keepString.equals(objectString))) {
		    if (DEBUG || false) System.err.println
			("=========== list info:\n" + objectString);
		    synchronized (saUrl) {
			array = new JSONArray(objectString);
		    }
		    String s = getRrmInfo(true);
		    keepString = objectString;

		    if (DEBUG || true) System.err.println
			("=========== arrayinfo:\n" + array.toString());
		}
	    } catch (Exception ex) {
		ex.printStackTrace();
	    }

	    try {
		Thread.sleep(INTERVAL);
	    } catch (Exception ex) {}
	}
    }

    public String makeRrm(String s)
    {
	s = makeRrmHead(s);
	// s = makeRrmFibre(s);

	synchronized(saUrl) {
	    makeRrmResv();
	}

	for (Enumeration e = rrmxml.keys(); e.hasMoreElements();) {
	    String key =  (String) e.nextElement();
	    String xml = rrmxml.get(key);
	    if (xml == null || xml.equals("")) continue;
	    
	    s += " <resourceSet id=\"" + key + "\" distinguishedName=\"\">\n";
	    s += xml;
	    s += " </resourceSet>\n";
	}
	s = makeRrmTail(s);
	return s;
    }

    public void makeRrmResv()
    {
	rrmxml = new Hashtable<String, String>();

	if (array == null) return;
	int length = array.length();
	for (int i = 0; i < length; i ++) {
	    JSONArray a = null;
	    try {
		a = array.getJSONArray(i);
		if (a == null) continue;

		JSONObject oo = a.getJSONObject(0);
		/*
		s += " <resourceSet id=\"" + oo.getString("resvId") + 
		    "\" distinguishedName=\"\">\n";
		*/
	    } catch (Exception ex) {
		ex.printStackTrace();
		continue;
	    }

	    for (int ii = 0; ii < a.length(); ii ++) {
		try {
		    JSONObject o = a.getJSONObject(ii);
		    if (o == null) continue;

		    String aurn = o.getString("a");
		    String zurn = o.getString("z");
		    if ((aurn == null) || (zurn == null)) {
			System.err.println("====== BAD: aurn=" + aurn + ", zurn=" + zurn);
			continue;
		    }
		    
		    String ap = alias.get(aurn);
		    String zp = alias.get(zurn);
		    if ((ap == null) || (zp == null)) {
			System.err.println("====== BAD: aurn=" + aurn + ", zurn=" + zurn);
			System.err.println("====== BAD: ap=" + ap + ", zp=" + zp);
			continue;
		    }

		    if (ap.equals(zp)) {
			if (DEBUG) {
			    System.err.println
				("====== SAME: aurn=" + aurn + ", zurn=" + zurn);
			    System.err.println
				("====== SAME: ap=" + ap + ", zp=" + zp);
			}
			continue;
		    }
		    
		    String st = o.getString("up");
		    String et = o.getString("dn");
		    if ((st == null) || (et == null)) {
			System.err.println("====== BAD: st=" + st + ", et=" + et);
			continue;
		    }
		    Date sdate = df2.parse(st);
		    Date edate = df2.parse(et);
		    Calendar scal = Calendar.getInstance();
		    scal.setTime(sdate);
		    Calendar ecal = Calendar.getInstance();
		    ecal.setTime(edate);
		    Calendar ccal = Calendar.getInstance();
		    ccal.setTime(new Date());

		    StringBuffer ssb = new StringBuffer();
		    ssb = df.format(scal.getTime(), ssb, new FieldPosition(0));
		    st = ssb.toString();

		    StringBuffer esb = new StringBuffer();
		    esb = df.format(ecal.getTime(), esb, new FieldPosition(0));
		    et = esb.toString();
		    
		    StringBuffer csb = new StringBuffer();
		    csb = df.format(ccal.getTime(), csb, new FieldPosition(0));
		    String ct = csb.toString();

		    String ostatus = null;
		    try {
			ostatus = o.getString("opStatus");
		    } catch (Exception exx) {}
		    if (ostatus == null) {
			System.err.println
			    ("====== opSatus=null: ap=" + ap + ", zp=" + zp);
			continue;
		    }
		    
		    int bw = o.getInt("bandwidth");
		    String status = "Reserved";
		    
		    String wait = "";
		    if (ostatus.equals("ACTIVE")) {
			status = "Activated";
			bw = BW_ACTIVE;
			
			if (ccal.after(ecal)) {
			    status = "Activated";
			    bw = BW_ACTIVE_WAIT;
			    wait = " to teardown";

			    if (DEBUG || true) {
				System.err.println
				    ("====== INFO: set DEACTIVE_WAIT:ap=" + ap +
				     ", zp=" + zp + "\n\tnow=" + ct + "\n\tend=" + et);
			    }
			} 
		    } else if (ccal.after(scal) && ccal.before(ecal)) {
			status = "Activated";
			bw = BW_ACTIVE_WAIT;
			wait = " to setup";

			if (DEBUG || true) {
			    System.err.println
				("====== INFO: set ACTIVE_WAIT:ap=" + ap +
				 ", zp=" + zp + "\n\tnow=" + ct + 
				 "\n\tsta=" + st + "\n\tend=" + et);
			}
		    } else {
			if (false) {
			    status = "Activated";
			    bw = BW_ACTIVE_WAIT;
			} else {
			    continue;
			}
		    }

		    long delt = scal.getTimeInMillis() - ecal.getTimeInMillis();
		    if (delt >= 0) {
			System.err.println
			    ("====== INFO: start time => end time: ap=" + ap + 
			     ", zp=" + zp + "\n\tsta=" + st + "\n\tend=" + et);
		    }

		    String id = "VLAN-0";
		    try {
			// id = "VLAN-" + o.getString("aVlan") + wait;
			id = "VLAN-" + o.getString("aVlan");
		    } catch (Exception ex) {}

		    String s = "    <networkResource APoint=\"" + ap + 
			"\" ZPoint=\"" + zp + "\">\n";
		    s += "      <path name=\"" + o.getString("network") + 
			"\" reservationID=\"" + o.getString("resvId") + "\">\n";
		    s += "        <startTime value=\"" + st + "\"/>\n";
		    s += "        <endTime value=\"" + et + "\"/>\n";
		    s += "        <reservationStatus value=\"" + status + "\"/>\n";
		    s += "        <aPoint value=\"" + ap + "\"/>\n";
		    s += "        <zPoint value=\"" + zp + "\"/>\n";
		    s += "        <bandwidth value=\"" + bw + "\"/>\n";
		    s += "      </path>\n";
		    s += "    </networkResource>\n";

		    String keep = rrmxml.get(id);
		    if (keep == null) {
			keep = s;
		    } else {
			keep += s;
		    }
		    rrmxml.put(id, keep);
		    // System.err.println("key=" + id + ", o=" + keep);
		} catch (Exception ex) {
		    ex.printStackTrace();
		    continue;
		}
	    }
	    /*
	    s += " </resourceSet>\n";
	    */
	}
	return;
    }

    public String makeRrmFibre(String s) 
    {
	Calendar startTime = Calendar.getInstance();
	startTime.setTime(new Date());
	startTime.set(Calendar.MILLISECOND, 0);
	Calendar endTime = (Calendar) startTime.clone();
	startTime.add(Calendar.DATE, -1);
	endTime.add(Calendar.DATE, 1);

	GregorianCalendar startgc = (GregorianCalendar) GregorianCalendar.getInstance();
	Date startDate = startTime.getTime();
	startgc.setTime(startDate);

	GregorianCalendar endgc = (GregorianCalendar) GregorianCalendar.getInstance();
	Date endDate = endTime.getTime();
	endgc.setTime(endDate);

	DatatypeFactory df = null;
	DatatypeFactory df2 = null;

	try {
	     df = DatatypeFactory.newInstance();
	     df2 = DatatypeFactory.newInstance();

	     XMLGregorianCalendar startxgc = df.newXMLGregorianCalendar(startgc);
	     XMLGregorianCalendar endxgc = df.newXMLGregorianCalendar(endgc);

	     if (fibre == null) return s;
	     
	     s += "  <resourceSet id=\"FIBRE\" distinguishedName=\"\">\n";
	     
	     for (int i = 0; i < fibre.length(); i++) {
		 JSONObject o = fibre.getJSONObject(i);
		 if (o == null) continue;
		 
		 String ap = o.getString("a");
		 String zp = o.getString("z");
		 if ((ap == null) || (zp == null)) continue;
		 
		 s += "    <networkResource APoint=\"" + ap + "\" ZPoint=\"" + zp + "\">\n";
		 s += "      <path name=\"fibre\" reservationID=\"FIX\">\n";
		 String ss = startxgc.toXMLFormat();
		 s += "        <startTime value=\"" + ss.replace(".000Z", "Z") + "\"/>\n";
		 String se = endxgc.toXMLFormat();
		 s += "        <endTime value=\"" + se.replace(".000Z", "Z") + "\"/>\n";
		 s += "        <reservationStatus value=\"Activated\"/>\n";
		 s += "        <aPoint value=\"" + ap + "\"/>\n";
		 s += "        <zPoint value=\"" + zp + "\"/>\n";
		 s += "        <bandwidth value=\"" + fibreBW + "\"/>\n";
		 s += "      </path>\n"; 
		 s += "    </networkResource>\n";
	     }
	
	     s += "  </resourceSet>\n";
	} catch (Exception ex) {
	    ex.printStackTrace();
	    return s;
	}
	return s;
    }

    public String makeRrmHead(String s) 
    {
	s =  "<?xml version=\"1.0\"?>\n";
	s += "<reservationIDMaps xmlns=\"http://glambda/schema/2006/07/demo\"\n";
	s += "  xmlns:demo=\"http://glambda/schema/2006/07/demo\"\n";
	s += "  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n";
	s += "  xsi:schemaLocation=\"http://glambda/schema/2006/07/demo demo.xsd\">\n";
	s += "<reservationIDMap sched=\"AIST-GRS\">\n";
	return s;
    }

    public String makeRrmTail(String s) 
    {
	s += "</reservationIDMap>\n";
	s += "</reservationIDMaps>\n";
	return s;
    }

    public void dumpObject(JSONObject o) 
    {
	String s = "Dump JSONObject:";
	if (o == null) {
	    System.err.println(s + "null");
	    return;
	}

	Iterator it = o.keys();
	
	while (it.hasNext()) {
	    String key= (String) it.next();

	    Object oo = o.optString(key);
	    if (oo == null) {
		s += "\n\t" + key + ":null";
	    } else if (oo instanceof String) {
		s += "\n\t" + key + ":" + (String) oo;
	    } else if (oo instanceof Integer) {
		s += "\n\t" + key + ":" + (Integer) oo;
	    } else {
		s += "\n\t" + key + ":" + "unknown type";
	    }
	}
	System.err.println(s);
    }

    public synchronized String getRrmInfo(boolean force)
    {
	Date d = new Date();
	if (force || (rrminfo == null) || (maked == null) || 
	    (d.getTime() - maked.getTime() > remake)) {
	    
	    maked = d;
	    rrminfo = null;
	    rrminfo = makeRrm(rrminfo);
	    if (DEBUG || true) System.err.println("====== rrmxml: " + force);
	    if (DEBUG || true) System.err.println(rrminfo);
	}
	return rrminfo;
    }

    static public void main (String[] args) 
    {
	try {
	    System.err.println("start Proxy");

	    Proxy p = Proxy.getInstance();

	    while(true) {
		Thread.sleep(INTERVAL);
	    }
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }
}
